package com.micro.ai.auth.service.impl;

import com.micro.ai.auth.dto.LoginRequest;
import com.micro.ai.auth.dto.PhoneLoginRequest;
import com.micro.ai.auth.dto.TokenResponse;
import com.micro.ai.auth.dto.UserDTO;
import com.micro.ai.auth.entity.LoginLog;
import com.micro.ai.auth.entity.Menu;
import com.micro.ai.auth.entity.Role;
import com.micro.ai.auth.entity.RoleMenu;
import com.micro.ai.auth.entity.User;
import com.micro.ai.auth.entity.UserSession;
import com.micro.ai.auth.mapper.LoginLogMapper;
import com.micro.ai.auth.mapper.MenuMapper;
import com.micro.ai.auth.mapper.RoleMapper;
import com.micro.ai.auth.mapper.RoleMenuMapper;
import com.micro.ai.auth.mapper.UserMapper;
import com.micro.ai.auth.mapper.UserSessionMapper;
import com.micro.ai.auth.service.AuthService;
import com.micro.ai.auth.service.CaptchaService;
import com.micro.ai.auth.service.SmsService;
import com.micro.ai.auth.service.TokenBlacklistService;
import com.micro.ai.commons.util.JwtUtils;
import com.micro.ai.commons.exception.BusinessException;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

/**
 * 认证服务实现
 * 
 * @author micro-ai
 * @since 0.0.1
 */
@Slf4j
@Service
public class AuthServiceImpl implements AuthService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RoleMapper roleMapper;

    @Autowired
    private MenuMapper menuMapper;

    @Autowired
    private RoleMenuMapper roleMenuMapper;

    @Autowired
    private LoginLogMapper loginLogMapper;

    @Autowired
    private UserSessionMapper userSessionMapper;

    @Autowired
    private JwtUtils jwtUtils;

    @Autowired
    private PasswordEncoder passwordEncoder;

    @Autowired
    private TokenBlacklistService tokenBlacklistService;

    @Autowired
    private SmsService smsService;

    @Autowired
    private CaptchaService captchaService;

    // Token映射表：sessionId -> 完整的JWT token（用于黑名单机制）
    private final Map<String, String> sessionTokenMap = new ConcurrentHashMap<>();

    @Override
    public TokenResponse login(LoginRequest request, String ipAddress, String userAgent) {
        try {
            // 1. 验证验证码（如果提供了验证码）
            if (request.getCaptcha() != null && !request.getCaptcha().isEmpty()) {
                if (request.getCaptchaKey() == null || request.getCaptchaKey().isEmpty()) {
                    throw new BusinessException("A0400", "验证码KEY不能为空");
                }
                
                boolean captchaValid = captchaService.validateCaptcha(
                    request.getCaptchaKey(), 
                    request.getCaptcha()
                );
                
                if (!captchaValid) {
                    recordLoginLog(request.getTenantId(), request.getUsername(), "failed", "验证码错误", request.getDeviceId(), request.getDeviceName(), ipAddress, userAgent);
                    throw new BusinessException("A0400", "验证码错误或已过期");
                }
            }
            
            User user = null;
            
            // 2. 查询用户（根据tenantId是否提供决定查询方式）
            if (request.getTenantId() != null && !request.getTenantId().isEmpty()) {
                // 提供了租户ID，按租户和用户名查询
                user = userMapper.selectByTenantAndUsername(
                    request.getTenantId(), 
                    request.getUsername()
                );
                
                if (user == null) {
                    // 尝试用邮箱查询
                    user = userMapper.selectByTenantAndEmail(
                        request.getTenantId(),
                        request.getUsername()
                    );
                }
            } else {
                // 未提供租户ID，仅按用户名查询（跨租户）
                user = userMapper.selectByUsername(request.getUsername());
                
                if (user == null) {
                    // 尝试用邮箱查询
                    user = userMapper.selectByEmail(request.getUsername());
                }
                
                if (user != null) {
                    log.info("未提供租户ID，使用用户所属租户: tenantId={}", user.getTenantId());
                }
            }

            if (user == null) {
                recordLoginLog(null, request, "failed", "用户不存在", ipAddress, userAgent);
                throw new BusinessException("A0002", "用户名或密码错误");
            }

            // 3. 验证密码
            if (!passwordEncoder.matches(request.getPassword(), user.getPasswordHash())) {
                // 增加失败次数
                incrementFailedAttempts(user);
                recordLoginLog(user, request, "failed", "密码错误", ipAddress, userAgent);
                throw new BusinessException("A0003", "用户名或密码错误");
            }

            // 4. 检查账户状态
            if (!"active".equals(user.getStatus())) {
                recordLoginLog(user, request, "blocked", "账户未激活或已被禁用", ipAddress, userAgent);
                throw new BusinessException("A0004", "账户未激活或已被禁用");
            }

            // 5. 检查账户锁定
            if (user.getLockedUntil() != null && user.getLockedUntil().isAfter(LocalDateTime.now())) {
                recordLoginLog(user, request, "blocked", "账户已被锁定", ipAddress, userAgent);
                throw new BusinessException("A0005", "账户已被锁定，请稍后再试");
            }

            // 6. 查询用户角色
            List<Role> roles = roleMapper.selectByUserId(user.getId());
            List<String> roleCodes = roles.stream()
                    .map(Role::getCode)
                    .collect(Collectors.toList());

            // 7. 查询用户的菜单权限
            // 格式：menu_code，例如：system_user, system_role, system_menu
            List<String> permissionCodes = buildMenuButtonPermissions(user.getId());

            // 8. 生成JWT令牌
            String accessToken = jwtUtils.generateAccessToken(
                user.getId(),
                user.getTenantId(),
                user.getUsername(),
                roleCodes,
                permissionCodes
            );

            String refreshToken = jwtUtils.generateRefreshToken(user.getId());

            // 9. 更新用户登录信息
            updateUserLoginInfo(user);

            // 10. 重置失败次数
            resetFailedAttempts(user);

            // 11. 记录登录日志
            recordLoginLog(user, request, "success", null, ipAddress, userAgent);

            // 12. 创建用户会话记录
            createUserSession(user, accessToken, refreshToken, ipAddress, userAgent);

            // 13. 构建用户DTO
            UserDTO userDTO = UserDTO.builder()
                    .id(user.getId())
                    .tenantId(user.getTenantId())
                    .username(user.getUsername())
                    .email(user.getEmail())
                    .displayName(user.getDisplayName())
                    .avatarUrl(user.getAvatarUrl())
                    .status(user.getStatus())
                    .roles(roleCodes)
                    .permissions(permissionCodes)
                    .build();

            // 13. 构建响应
            return TokenResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .tokenType("Bearer")
                    .expiresIn(jwtUtils.getAccessTokenExpiration())
                    .user(userDTO)
                    .build();

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("登录失败", e);
            throw new BusinessException("B0001", "系统异常，请稍后再试");
        }
    }

    @Override
    public TokenResponse phoneLogin(PhoneLoginRequest request, String ipAddress, String userAgent) {
        try {
            // 1. 验证短信验证码
            if (!smsService.validateSmsCode(request.getPhone(), request.getSmsCode())) {
                recordPhoneLoginLog(null, request, "failed", "验证码错误或已过期", ipAddress, userAgent);
                throw new BusinessException("A0007", "验证码错误或已过期");
            }

            User user = null;
            
            // 2. 查询用户（根据tenantId是否提供决定查询方式）
            if (request.getTenantId() != null && !request.getTenantId().isEmpty()) {
                // 提供了租户ID，按租户和手机号查询
                user = userMapper.selectByTenantAndPhone(
                    request.getTenantId(), 
                    request.getPhone()
                );
            } else {
                // 未提供租户ID，仅按手机号查询（跨租户）
                user = userMapper.selectByPhone(request.getPhone());
                
                if (user != null) {
                    log.info("未提供租户ID，使用用户所属租户: tenantId={}", user.getTenantId());
                }
            }

            if (user == null) {
                recordPhoneLoginLog(null, request, "failed", "手机号未注册", ipAddress, userAgent);
                throw new BusinessException("A0008", "手机号未注册");
            }

            // 3. 检查账户状态
            if (!"active".equals(user.getStatus())) {
                recordPhoneLoginLog(user, request, "blocked", "账户未激活或已被禁用", ipAddress, userAgent);
                throw new BusinessException("A0004", "账户未激活或已被禁用");
            }

            // 4. 检查账户锁定
            if (user.getLockedUntil() != null && user.getLockedUntil().isAfter(LocalDateTime.now())) {
                recordPhoneLoginLog(user, request, "blocked", "账户已被锁定", ipAddress, userAgent);
                throw new BusinessException("A0005", "账户已被锁定，请稍后再试");
            }

            // 5. 查询用户角色
            List<Role> roles = roleMapper.selectByUserId(user.getId());
            List<String> roleCodes = roles.stream()
                    .map(Role::getCode)
                    .collect(Collectors.toList());

            // 6. 查询用户的菜单权限
            // 格式：menu_code，例如：system_user, system_role, system_menu
            List<String> permissionCodes = buildMenuButtonPermissions(user.getId());

            // 7. 生成JWT令牌
            String accessToken = jwtUtils.generateAccessToken(
                user.getId(),
                user.getTenantId(),
                user.getUsername(),
                roleCodes,
                permissionCodes
            );

            String refreshToken = jwtUtils.generateRefreshToken(user.getId());

            // 8. 更新用户登录信息
            updateUserLoginInfo(user);

            // 9. 重置失败次数
            resetFailedAttempts(user);

            // 10. 记录登录日志
            recordPhoneLoginLog(user, request, "success", null, ipAddress, userAgent);

            // 11. 创建用户会话记录
            createUserSession(user, accessToken, refreshToken, ipAddress, userAgent);

            // 12. 构建用户DTO
            UserDTO userDTO = UserDTO.builder()
                    .id(user.getId())
                    .tenantId(user.getTenantId())
                    .username(user.getUsername())
                    .email(user.getEmail())
                    .displayName(user.getDisplayName())
                    .avatarUrl(user.getAvatarUrl())
                    .phone(user.getPhone())
                    .status(user.getStatus())
                    .roles(roleCodes)
                    .permissions(permissionCodes)
                    .build();

            // 12. 构建响应
            return TokenResponse.builder()
                    .accessToken(accessToken)
                    .refreshToken(refreshToken)
                    .tokenType("Bearer")
                    .expiresIn(jwtUtils.getAccessTokenExpiration())
                    .user(userDTO)
                    .build();

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("手机号登录失败", e);
            throw new BusinessException("B0001", "系统异常，请稍后再试");
        }
    }

    @Override
    public void logout(String token) {
        try {
            // 提取用户ID
            String userId = jwtUtils.getUserId(token);
            if (userId == null) {
                throw new BusinessException("A0006", "无效的令牌");
            }

            // 将令牌加入黑名单
            long expiresIn = jwtUtils.getAccessTokenExpiration();
            tokenBlacklistService.addToBlacklist(token, expiresIn);

            // 清理用户会话记录
            clearUserSession(token);

            log.info("用户登出成功: userId={}", userId);

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("登出失败", e);
            throw new BusinessException("B0001", "系统异常");
        }
    }

    @Override
    public TokenResponse refreshToken(String refreshToken) {
        try {
            // 1. 验证刷新令牌
            String userId = jwtUtils.getUserId(refreshToken);
            if (userId == null) {
                throw new BusinessException("A0006", "无效的刷新令牌");
            }

            // 2. 查询用户
            User user = userMapper.selectById(userId);
            if (user == null) {
                throw new BusinessException("A0002", "用户不存在");
            }

            // 3. 检查账户状态
            if (!"active".equals(user.getStatus())) {
                throw new BusinessException("A0004", "账户未激活或已被禁用");
            }

            // 4. 查询用户角色和权限
            List<Role> roles = roleMapper.selectByUserId(user.getId());
            List<String> roleCodes = roles.stream()
                    .map(Role::getCode)
                    .collect(Collectors.toList());

            // 查询用户的菜单权限
            List<String> permissionCodes = buildMenuButtonPermissions(user.getId());

            // 5. 生成新的令牌
            String newAccessToken = jwtUtils.generateAccessToken(
                user.getId(),
                user.getTenantId(),
                user.getUsername(),
                roleCodes,
                permissionCodes
            );

            String newRefreshToken = jwtUtils.generateRefreshToken(user.getId());

            // 6. 构建用户DTO
            UserDTO userDTO = UserDTO.builder()
                    .id(user.getId())
                    .tenantId(user.getTenantId())
                    .username(user.getUsername())
                    .email(user.getEmail())
                    .displayName(user.getDisplayName())
                    .avatarUrl(user.getAvatarUrl())
                    .status(user.getStatus())
                    .roles(roleCodes)
                    .permissions(permissionCodes)
                    .build();

            // 7. 构建响应
            return TokenResponse.builder()
                    .accessToken(newAccessToken)
                    .refreshToken(newRefreshToken)
                    .tokenType("Bearer")
                    .expiresIn(jwtUtils.getAccessTokenExpiration())
                    .user(userDTO)
                    .build();

        } catch (BusinessException e) {
            throw e;
        } catch (Exception e) {
            log.error("刷新令牌失败", e);
            throw new BusinessException("B0001", "系统异常");
        }
    }

    @Override
    public boolean validateToken(String token) {
        try {
            // 检查令牌是否在黑名单中
            if (tokenBlacklistService.isBlacklisted(token)) {
                log.warn("令牌已被加入黑名单: token={}", token.substring(0, 20) + "...");
                return false;
            }

            return jwtUtils.validateToken(token) != null && !jwtUtils.isTokenExpired(token);
        } catch (Exception e) {
            log.error("验证令牌失败", e);
            return false;
        }
    }

    /**
     * 更新用户登录信息
     */
    private void updateUserLoginInfo(User user) {
        user.setLastLoginAt(LocalDateTime.now());
        user.setLastActiveAt(LocalDateTime.now());
        // TODO: 设置登录IP
        // user.setLastLoginIp(RequestUtils.getClientIp());
        userMapper.updateById(user);
    }

    /**
     * 增加失败次数
     */
    private void incrementFailedAttempts(User user) {
        Integer attempts = user.getFailedLoginAttempts() == null ? 0 : user.getFailedLoginAttempts();
        attempts++;
        user.setFailedLoginAttempts(attempts);

        // 如果失败次数达到5次，锁定账户30分钟
        if (attempts >= 5) {
            user.setLockedUntil(LocalDateTime.now().plusMinutes(30));
        }

        userMapper.updateById(user);
    }

    /**
     * 重置失败次数
     */
    private void resetFailedAttempts(User user) {
        user.setFailedLoginAttempts(0);
        user.setLockedUntil(null);
        userMapper.updateById(user);
    }

    /**
     * 记录登录日志
     */
    private void recordLoginLog(User user, LoginRequest request, String status, String failureReason, String ipAddress, String userAgent) {
        LoginLog log = new LoginLog();
        log.setUserId(user != null ? user.getId() : null);
        log.setTenantId(request.getTenantId() != null ? request.getTenantId() : (user != null ? user.getTenantId() : null));
        log.setUsername(request.getUsername());
        log.setLoginType("password");
        log.setStatus(status);
        log.setFailureReason(failureReason);
        
        // 设置设备信息
        log.setDeviceId(request.getDeviceId());
        log.setDeviceName(request.getDeviceName());
        log.setDeviceType(request.getDeviceName() != null && request.getDeviceName().toLowerCase().contains("mobile") ? "mobile" : "web");
        
        // 设置网络信息
        log.setIpAddress(ipAddress);
        log.setUserAgent(userAgent);
        // TODO: 基于IP获取地理位置
        // log.setLocation(LocationUtils.getLocation(ipAddress));
        
        loginLogMapper.insert(log);
    }
    
    /**
     * 记录登录日志（用于验证码错误等无用户对象的情况）
     */
    private void recordLoginLog(String tenantId, String username, String status, String failureReason, String deviceId, String deviceName, String ipAddress, String userAgent) {
        LoginLog log = new LoginLog();
        log.setUserId(null);
        log.setTenantId(tenantId);
        log.setUsername(username);
        log.setLoginType("password");
        log.setStatus(status);
        log.setFailureReason(failureReason);
        
        // 设置设备信息
        log.setDeviceId(deviceId);
        log.setDeviceName(deviceName);
        log.setDeviceType(deviceName != null && deviceName.toLowerCase().contains("mobile") ? "mobile" : "web");
        
        // 设置网络信息
        log.setIpAddress(ipAddress);
        log.setUserAgent(userAgent);
        
        loginLogMapper.insert(log);
    }

    /**
     * 记录手机号登录日志
     */
    private void recordPhoneLoginLog(User user, PhoneLoginRequest request, String status, String failureReason, String ipAddress, String userAgent) {
        LoginLog log = new LoginLog();
        log.setUserId(user != null ? user.getId() : null);
        log.setTenantId(request.getTenantId() != null ? request.getTenantId() : (user != null ? user.getTenantId() : null));
        log.setUsername(user != null ? user.getUsername() : request.getPhone());
        log.setLoginType("sms");
        log.setStatus(status);
        log.setFailureReason(failureReason);
        
        // 设置设备信息
        log.setDeviceId(request.getDeviceId());
        log.setDeviceName(request.getDeviceName());
        log.setDeviceType(request.getDeviceName() != null && request.getDeviceName().toLowerCase().contains("mobile") ? "mobile" : "web");
        
        // 设置网络信息
        log.setIpAddress(ipAddress);
        log.setUserAgent(userAgent);
        // TODO: 基于IP获取地理位置
        // log.setLocation(LocationUtils.getLocation(ipAddress));
        
        loginLogMapper.insert(log);
    }

    /**
     * 构建用户的按钮权限列表
     * 只提取 type='button' 的菜单作为权限
     * 格式：button_code
     * 例如：["user:view", "user:create", "role:update", "menu:delete"]
     * 
     * @param userId 用户ID
     * @return 权限编码列表（按钮权限编码列表）
     */
    private List<String> buildMenuButtonPermissions(String userId) {
        List<String> permissions = new ArrayList<>();
        
        try {
            // 1. 查询用户的所有角色菜单关联
            List<RoleMenu> roleMenus = roleMenuMapper.selectByUserId(userId);
            
            if (roleMenus == null || roleMenus.isEmpty()) {
                return permissions;
            }
            
            // 2. 收集所有菜单ID（去重）
            Set<String> menuIds = roleMenus.stream()
                    .map(RoleMenu::getMenuId)
                    .collect(Collectors.toSet());
            
            // 3. 批量查询菜单信息
            if (!menuIds.isEmpty()) {
                List<Menu> menus = menuMapper.selectBatchIds(new ArrayList<>(menuIds));
                
                // 4. 只提取 type='button' 的菜单编码作为权限
                permissions = menus.stream()
                        .filter(menu -> "button".equals(menu.getType()))  // 只要按钮类型
                        .filter(menu -> menu.getPermissionCode() != null && !menu.getPermissionCode().isEmpty())
                        .map(Menu::getPermissionCode)
                        .distinct()
                        .collect(Collectors.toList());
            }
            
            log.debug("用户 {} 的按钮权限: {}", userId, permissions);
            
        } catch (Exception e) {
            log.error("构建用户按钮权限失败: userId={}, error={}", userId, e.getMessage(), e);
        }
        
        return permissions;
    }

    /**
     * 创建用户会话记录
     */
    private void createUserSession(User user, String accessToken, String refreshToken, String ipAddress, String userAgent) {
        try {
            // 生成会话ID
            String sessionId = UUID.randomUUID().toString().replace("-", "");
            
            // 检测设备类型
            String deviceType = detectDeviceType(userAgent);
            String deviceName = extractDeviceName(userAgent);
            String deviceId = extractDeviceId(userAgent);
            
            // 处理token长度问题 - 如果token超过255字符，使用hash值
            String sessionToken = accessToken;
            String refreshTokenValue = refreshToken;
            
            if (accessToken != null && accessToken.length() > 255) {
                // 如果token太长，使用hash值作为唯一标识
                sessionToken = generateTokenHash(accessToken);
                log.warn("Access token too long ({} chars), using hash: {}", accessToken.length(), sessionToken);
            }
            
            if (refreshToken != null && refreshToken.length() > 255) {
                refreshTokenValue = generateTokenHash(refreshToken);
                log.warn("Refresh token too long ({} chars), using hash: {}", refreshToken.length(), refreshTokenValue);
            }
            
            // 创建会话记录
            UserSession session = new UserSession();
            session.setId(sessionId);
            session.setUserId(user.getId());
            session.setTenantId(user.getTenantId());
            session.setSessionToken(sessionToken);
            session.setRefreshToken(refreshTokenValue);
            session.setDeviceId(deviceId);
            session.setDeviceName(deviceName);
            session.setDeviceType(deviceType);
            session.setIpAddress(ipAddress);
            session.setUserAgent(userAgent);
            session.setLocation(getLocationByIP(ipAddress)); // TODO: 实现IP定位
            session.setLastActiveAt(LocalDateTime.now());
            session.setExpiresAt(LocalDateTime.now().plusHours(24)); // 24小时过期
            session.setCreatedAt(LocalDateTime.now());
            
            // 将完整的JWT token存储到映射表中（用于黑名单机制）
            sessionTokenMap.put(sessionId, accessToken);
            
            // 保存会话记录
            userSessionMapper.insert(session);
            
            log.info("创建用户会话记录成功: userId={}, sessionId={}, deviceType={}", 
                user.getId(), sessionId, deviceType);
                
        } catch (Exception e) {
            log.error("创建用户会话记录失败: userId={}, error={}", user.getId(), e.getMessage(), e);
            // 不影响登录流程，只记录错误
        }
    }

    /**
     * 检测设备类型
     * 优先级：mobile > tablet > web（浏览器）> desktop（桌面系统）> other
     */
    private String detectDeviceType(String userAgent) {
        if (userAgent == null) return "other";
        
        String ua = userAgent.toLowerCase();
        
        // 优先检测移动设备
        if (ua.contains("mobile") || ua.contains("android") || ua.contains("iphone")) {
            return "mobile";
        }
        
        // 检测平板设备
        if (ua.contains("tablet") || ua.contains("ipad")) {
            return "tablet";
        }
        
        // 优先检测浏览器标识（web），而不是操作系统
        // 如果有浏览器标识，说明是通过浏览器访问，归类为 web
        if (ua.contains("mozilla") || ua.contains("chrome") || ua.contains("safari") || 
            ua.contains("firefox") || ua.contains("edge") || ua.contains("opera")) {
            return "web";
        }
        
        // 检测桌面操作系统（但没有浏览器标识的情况，比如桌面应用）
        if (ua.contains("windows") || ua.contains("macintosh") || ua.contains("linux")) {
            return "desktop";
        }
        
        return "other";
    }

    /**
     * 提取设备名称
     */
    private String extractDeviceName(String userAgent) {
        if (userAgent == null) return "未知设备";
        
        String ua = userAgent.toLowerCase();
        if (ua.contains("chrome")) {
            return "Chrome浏览器";
        } else if (ua.contains("firefox")) {
            return "Firefox浏览器";
        } else if (ua.contains("safari")) {
            return "Safari浏览器";
        } else if (ua.contains("edge")) {
            return "Edge浏览器";
        } else if (ua.contains("android")) {
            return "Android设备";
        } else if (ua.contains("iphone")) {
            return "iPhone";
        } else if (ua.contains("ipad")) {
            return "iPad";
        } else if (ua.contains("windows")) {
            return "Windows设备";
        } else if (ua.contains("macintosh")) {
            return "Mac设备";
        }
        return "未知设备";
    }

    /**
     * 提取设备ID（简化实现）
     */
    private String extractDeviceId(String userAgent) {
        if (userAgent == null) return null;
        // 使用User-Agent的hash作为设备ID（简化实现）
        return String.valueOf(userAgent.hashCode());
    }

    /**
     * 获取完整的JWT token（用于黑名单机制）
     */
    public String getFullJwtToken(String sessionId) {
        return sessionTokenMap.get(sessionId);
    }

    /**
     * 移除token映射（会话结束时清理）
     */
    public void removeTokenMapping(String sessionId) {
        sessionTokenMap.remove(sessionId);
    }

    /**
     * 生成token的hash值（用于存储长token）
     */
    private String generateTokenHash(String token) {
        try {
            java.security.MessageDigest digest = java.security.MessageDigest.getInstance("SHA-256");
            byte[] hash = digest.digest(token.getBytes(java.nio.charset.StandardCharsets.UTF_8));
            StringBuilder hexString = new StringBuilder();
            for (byte b : hash) {
                String hex = Integer.toHexString(0xff & b);
                if (hex.length() == 1) {
                    hexString.append('0');
                }
                hexString.append(hex);
            }
            return hexString.toString();
        } catch (Exception e) {
            log.error("生成token hash失败: {}", e.getMessage());
            // 降级方案：使用token的前32个字符
            return token.substring(0, Math.min(32, token.length()));
        }
    }

    /**
     * 清理用户会话记录
     */
    private void clearUserSession(String token) {
        try {
            // 根据会话令牌删除会话记录
            // 如果token太长，需要先计算hash值
            String searchToken = token;
            if (token != null && token.length() > 255) {
                searchToken = generateTokenHash(token);
            }
            
            userSessionMapper.delete(
                new LambdaQueryWrapper<UserSession>()
                    .eq(UserSession::getSessionToken, searchToken)
            );
            
            log.info("清理用户会话记录成功: token={}", searchToken);
            
        } catch (Exception e) {
            log.error("清理用户会话记录失败: token={}, error={}", token, e.getMessage(), e);
            // 不影响登出流程，只记录错误
        }
    }

    /**
     * 根据IP获取地理位置（TODO: 实现IP定位）
     */
    private String getLocationByIP(String ipAddress) {
        // TODO: 实现基于IP的地理位置查询
        // 可以使用第三方IP定位服务，如：ipapi.co, ipinfo.io等
        return "未知位置";
    }
}

